/* file: sdcard.c
 * author: mezzo forte
 * purpose: provides code for init. and comunication with
 * 			an SD card.
 * last edit: 2/17/2010 : Added this block
 */

#include "mfutils.h"
#include "data_types.h"
#include "sdcard.h"
#include "stdio.h"


unsigned char recvbuf[sd_BLOCKSIZE + 8];

/////////////////////////////////////////////
//
//	SD CARD
//
/////////////////////////////////////////////
void sd_init()
{
	char R1_Response = 0xFF;
	SYS_EXBUSSEL = (SYS_EXBUSSEL & 0x0fff) | 0x1000;
	SYS_PRCNTR = 0x04;
	SYS_PRCNTRLR = 0x0080;

    /* SPI Clock Control */
    SPI_SPICC2 = 0x0000; // SPI clock disabled
    SPI_SPICC1 = 0x03e7; // 12MHz input clock divided by an amount


    SPI_SPIDC1 = 0x0000; // SPI0 low idle clock, active low CS, shift out on rising clock, no clock delay
    SPI_SPIDC2 = 0x0000; 
    
    SPI_SPICC2 = 0x8000; // SPI clock enabled
    
    //Set SD card in SPI read mode
    
    wait_usec(10000);
    sd_init_cycle();
   
    //Step 1: Send RESET/IDLE command
    
    do
    {
    	R1_Response = SendCommand(GO_IDLE_STATE, 0);
    	//printf("R1_Resp = %x\n", R1_Response);
    	
    }while(R1_Response != 0x01);
    
    ReadByte();
    ReadByte();
    
    printf("Reset \n");
     
    do
    {
	    //Step 2: send command 55
	    R1_Response = SendCommand(A_COMMAND, 0);
	    //printf("In Loop = %x\n", R1_Response); 
	    
	    //Step 3: send command 41
	    R1_Response = SendCommand(A_GO_OP_CONDITION, 0);
	    //printf("In Loop = %x\n", R1_Response); 
	    
    }while(R1_Response != 0x00);
    printf("Init. Complete! \n");
    
}

char sd_cycle(Uint16 *buf, Uint16 len, char readonly)
{
    Uint16 i;
    char r1 = 0xFF;

    SPI_SPICR1 = 0x0000 | len + 7;
    i = SPI_SPIDR2;  // Dummy read

   	/* SD Card access cycle */
	for (i = 0; i < len + 8; i++)
	{
		if((i<len) && (!readonly))
			SPI_SPIDR2 = buf[i] << 8;
		else
			SPI_SPIDR2 = 0xFFFF;
			
		SPI_SPIDR1 = 0x0000;  
        SPI_SPICR2 = 0x1039;  //  8-bit words, read    Correct Chip Select
        while((SPI_SPISR1 & 0x0002) == 0){};
        buf[i] = SPI_SPIDR2 & 0xff;
        
        r1 = SD_R1_Response(r1, buf[i]);
	}
	
	
	
	/* Wait for transfer to complete */
//	while((SPI_SPISR1 & 0x0008) == 0) {};
	
	wait_usec(1000);
	
	return r1;
}

char ReadByte()
{
	Uint16 i;
	
	SPI_SPICR1 = 0x0000;
    i = SPI_SPIDR2;  // Dummy read

   	/* SD Card access cycle */
	SPI_SPIDR2 = 0x00FF;
	SPI_SPIDR1 = 0x0000;  
    SPI_SPICR2 = 0x1039;  //  8-bit words, read    Correct Chip Select
    while((SPI_SPISR1 & 0x0002) == 0) {};
    i = SPI_SPIDR2 & 0xff; 
    
	/* Wait for transfer to complete */
//	while((SPI_SPISR1 & 0x0008) == 0) {};
	
	wait_usec(10000);
	
	return (char)i;
}

void sd_init_cycle()
{
    Uint16 i;
    Uint16 r1[12];

    SPI_SPICR1 = 0x0000 | 10 - 1;
    i = SPI_SPIDR2;  // Dummy read

   	/* Spirom access cycle */
	for (i = 0; i < 10; i++)
	{
		SPI_SPIDR2 = 0x00FF;
		SPI_SPIDR1 = 0x0000;  
        SPI_SPICR2 = 0x2039;  //  8-bit words, read    Correct Chip Select
        while((SPI_SPISR1 & 0x0002) == 0) {};
        r1[i] = SPI_SPIDR2 & 0xff; 
	}
	
	/* Wait for transfer to complete */
//	while((SPI_SPISR1 & 0x0008) == 0) {};
	
	wait_usec(10000);
}

char SendCommand(Uint16 comm, unsigned long arg)
{
	sdbuf[0] = (0x40 | comm);
	sdbuf[1] = 0xFF & (arg >> 24);
	sdbuf[2] = 0xFF & (arg >> 16);
	sdbuf[3] = 0xFF & (arg >> 8);
	sdbuf[4] = 0xFF & (arg);
	sdbuf[5] = 0x95;

	
	return sd_cycle(sdbuf, 6, 0);
}

char SD_R1_Response(char r1, Uint16 buf)
{
	if(r1 != 0xFF)
		return r1;
	
	if((buf & 0x0080) == 0)
	   return buf;
		
	return 0xFF;
}

char sd_read_cycle(volatile unsigned char *buf, Uint16 len, char readonly, Uint16 offset)
{
    Uint16 i;


    SPI_SPICR1 = 0x0000 | (len + offset + 3); // + 4
    i = SPI_SPIDR2;  // Dummy read

   	/* Spirom access cycle */
	for (i = 0; i < len + 4; i++) // + 3
	{

		if((!readonly))
			SPI_SPIDR2 = buf[i] << 8;
		else
			SPI_SPIDR2 = 0xFFFF;
			
		SPI_SPIDR1 = 0x0000;  
        SPI_SPICR2 = 0x1039;  //  8-bit words, read    Correct Chip Select
        while((SPI_SPISR1 & 0x0002) == 0) {};
        buf[i] = SPI_SPIDR2 & 0xff;
        
	}
	
	//printf("%x|%x|%x|%x|%x|%x|%x|%x\n", buf[len], buf[len + 1], buf[len + 2], buf[len + 3], buf[len + 4], buf[len + 5], buf[len + 6], buf[len + 7]);
	
	/* Wait for transfer to complete */
//	while((SPI_SPISR1 & 0x0008) == 0) {};
	
	wait_usec(10000);
	
	return 1;
}

char sd_read_single_block(unsigned long start_block)
{
	char response;
	Uint16 timeout;
	
	response = SendCommand(READ_SINGLE_BLOCK, start_block<<9);
	
	if(response != 0)
	    return response;
	    
	timeout = 0;
	
	while(WaitForStartToken(timeout++) != 0xFE)
		if(timeout > 0xFE)
		    return 1;
		    
	sd_read_cycle(recvbuf, 512, 1, timeout);
	
	return 0;
}

char WaitForStartToken(Uint16 time)
{
	Uint16 i;
	
	SPI_SPICR1 = time + 1;
    i = SPI_SPIDR2;  // Dummy read

   	/* Spirom access cycle */
	SPI_SPIDR2 = 0x00FF;
	SPI_SPIDR1 = 0x0000;  
    SPI_SPICR2 = 0x1039;  //  8-bit words, read    Correct Chip Select
    while((SPI_SPISR1 & 0x0002) == 0) {};
    i = SPI_SPIDR2 & 0xff;
    
  // printf("Wait = %x\n", i); 

	/* Wait for transfer to complete */
//	while((SPI_SPISR1 & 0x0008) == 0) {};
	
	return (char)i;
}


